﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ComputerGraphics.Graphics;
using ComputerGraphics.Utils;

namespace ComputerGraphics.Geometry
{
    public class Tube : Shape
    {
        private Point3D center;
        private Point3D r;
        private Point3D n;

        public Tube(Point3D center, Point3D n, double radius, Texture texture)
            : base(texture)
        {
            this.center = center;
            this.n = n;
            
            Point3D refVec;

            if (MathUtils.GetZeroDouble(n.X) == 0)
                refVec = new Point3D(n.X + 10, n.Y, n.Z);
            else if (MathUtils.GetZeroDouble(n.Y) == 0)
                refVec = new Point3D(n.X, n.Y + 10, n.Z);
            else
                refVec = new Point3D(n.X, n.Y, n.Z + 10);

            this.r = (refVec ^ n).Direction * radius;

        }

        public override Intersection Intersect(Ray ray)
        {
            Point3D x = (ray.Origin - center) ^ n;
            Point3D y = ray.Direction ^ n;
            double right = r.SquareNorm * n.SquareNorm;
            double a = y.SquareNorm;
            double b = 2 * (x * y);
            double c = x.SquareNorm - right;

            double d = b * b - 4 * a * c;
            double[] t;

            if (MathUtils.GetZeroDouble(d) > 0) {
                t = new double[2];
                d = Math.Sqrt(d);
                t[0] = (-b + d) / 2 / a;
                t[1] = (-b - d) / 2 / a;
            }
            else if (MathUtils.GetZeroDouble(d) == 0)
            {
                t = new double[1];
                t[0] = -b / 2 / a;
            }
            else
                t = new double[0];

            List<Intersection> itss = new List<Intersection>();
            foreach (double dist in t)
            {
                if (MathUtils.GetZeroDouble(dist) <= 0) continue;
                Point3D p = ray.GetPointByDistance(dist);

                double dotProduct = (p - center) * (n.Direction);
                if (MathUtils.GetZeroDouble(dotProduct) < 0) continue;
                Point3D o = center + (n.Direction * dotProduct);

                dotProduct = (p - center - n) * (-n.Direction);
                if (MathUtils.GetZeroDouble(dotProduct) < 0) continue;

                itss.Add(new Intersection(ray, this, dist, p, (p - o).Direction));
            }

            double minDist = Double.MaxValue;
            Intersection ret = null;

            foreach (Intersection i in itss)
            {
                if (MathUtils.GetZeroDouble(i.Distance) > 0 && i.Distance < minDist)
                {
                    minDist = i.Distance;
                    ret = i;
                }
            }

            return ret;
        }

        public override Intersection InternalIntersect(Ray r)
        {
            Intersection ret = Intersect(r);

            if (ret != null) ret.Normal = -ret.Normal;

            return ret;
        }
    }
}
